/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Relationship.Simple.DataTypes;

import ResultEditor.Annotations.Annotation;
import ResultEditor.Annotations.NormalRelationship;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.Vector;

/**
 * This Class is used to hold all Attributes that are currently in the Schema.  Methods
 * for accessing and modifying these Attributes are also found in this class.
 * @see env.Attribute
 * @see ResultEditor.SimpleSchema.Editor
 * @author Chris
 */
public class AttributeList
{
    //<editor-fold defaultstate="collapsed" desc="Member Variables">
    /**
     * The list of attributes in the schema.
     */
    private TreeSet<Attribute> attSet = new TreeSet<Attribute>();
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Constructor">
    /**
     * Default constructor
     */
    public AttributeList(){}
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Public Methods">
    /**
     * Add an attribute to the Schema
     * @param toAdd - the Attribute to add
     */
    public void Add(Attribute toAdd)
    {
        if(toAdd.getName().equals(""))
            return;
        attSet.add(toAdd);
    }
    /**
     * Remove an attribute from the schema
     * @param toRemove - the attribute to remove.
     */
    public void Remove(Attribute toRemove)
    {
        attSet.remove(toRemove);
    }
    /**
     * Get all Attributes in the current schema.
     * @return - All Attributes in the current schema.
     */
    public ArrayList<Attribute> getAttributes()
    {
        ArrayList<Attribute> toReturn = new ArrayList<Attribute>();
        for(Attribute att: attSet)
            toReturn.add(att);
        return toReturn;
    }

    public void rename(String oldname, String newname) {

        if((oldname==null)||(oldname.trim().length()<1))
            return;
        if((newname==null)||(newname.trim().length()<1))
            return;

        if(attSet==null)
            return;

        if(exists(newname)){
            return;
        }


        for(Attribute att: attSet){
            if(att==null)
                continue;
            if(att.getName()==null)
                continue;
            if(att.getName().equals(oldname)){


                Attribute newatt = new Attribute( newname, att.getAllAllowedEntries() );
                this.Remove(att);
                this.Add(newatt);
                return;
            }
        }

    }

    private boolean exists(String attributeName){
        if((attributeName==null)||(attributeName.trim().length()<1))
            return true;
        
        for(Attribute att0: attSet){
            if(att0==null)
                continue;
            if(att0.getName()==null)
                continue;

            if(att0.getName().equals(attributeName.trim()))
                return true;
        }

        return false;
    }

    /**
     * Get the allowed Values for a given attribute name
     * @param s - the name of the attribute
     * @return - an ArrayList of allowed values
     */
    public ArrayList<String> getAllowed(String s)
    {
        ArrayList<String> toReturn = new ArrayList<String>();
        for(Attribute att: attSet)
        {
            if(att.getName().equals(s))
            {
                for(String allowed: att.getAllowedEntries())
                    toReturn.add(allowed);
            }
        }
        return toReturn;
    }
    /**
     * Check a collection of Annotations to see if there are any with Attributes outside
     * of the current Schema.
     * @param toCheck - the annotations to check
     * @return - all Attributes outside the current schema.
     */
    public TreeSet<Attribute> checkExists(Vector<Annotation> toCheck)
    {
        //Create a treeset to return all uniquely new attributes
        TreeSet<Attribute> toReturn = new TreeSet<Attribute>();

        //Loop through each annotation to find Attributes outside of our schema.
        for(Annotation anno: toCheck)
        {
            if (anno.normalrelationships == null)
                continue;
            
            //Loop through each normal relationship and compare it against our current schema
            //to see if our schema can represent it.
            for(NormalRelationship rel: anno.normalrelationships)
            {
                if(rel.relationshipName == null)
                    continue;
                if(rel.relationshipValue == null)
                    continue;
                //Make a copy because we will be modifying it.. and we don't want the changes to
                //affect the actual annotation.
                rel = rel.copy();
                for(Attribute att: attSet)
                {

                    //Remove any already accounted for values... need to check for same att name first
                    for(int i = 0; i< rel.relationshipValue.size(); i++)
                    {
                        if(!att.getName().equals(rel.relationshipName))
                            continue;
                        String value = rel.relationshipValue.get(i);
                        if(att.getAllowedEntries().contains(value))
                        {
                            rel.relationshipValue.remove(i);
                            i--;
                        }
                    }
                    //Need to somehow indicate that the name is accounted for
                    if(att.getName().equals(rel.relationshipName))
                        rel.deadName = true;
                }
                //If we end up with a relationship with values in this list than we have an unknown attribute
                //value.
                if(rel.relationshipValue.size() != 0)
                {
                    HashSet<String> values = new HashSet<String>();
                    values.addAll(rel.relationshipValue);
                    Attribute att = new Attribute(rel.relationshipName, values);
                    att.setRepeatName(!rel.deadName);
                    toReturn.add(att);
                }
            }

        }
        toReturn = combineSameName(toReturn);
        return toReturn;
    }
    /**
     * Add All attributes to the Schema.
     * @param toAdd
     */
    public void addAll(Collection<Attribute> toAdd)
    {
        //Copy to ArrayList... TODO probably more efficient way to do this... look into
        //that if this ends up being too slow.
        ArrayList<Attribute> initial = new ArrayList<Attribute>();
        ArrayList<Attribute> needToAdd = new ArrayList<Attribute>();
        initial.addAll(attSet);
        needToAdd.addAll(toAdd);
        //Loop through all attributes that we are trying to add to see if the Attribute already exists
        // and only the values are needed.
        for(Attribute att: needToAdd)
        {
            Attribute matching = null;
            for(Attribute init: initial)
            {
                if(init.getName().equals(att.getName()))
                    matching = init;
            }
            if(matching != null)
                matching.getAllowedEntries().addAll(att.getAllowedEntries());
            else
            {
                attSet.add(att);
            }
        }
    }
    //</editor-fold>
    
    //<editor-fold defaultstate="collapsed" desc="Private Methods">
    /**
     * Combine a set of attributes so that any attributes that have the same name are combined into
     * Attribute object.  This is done by adding the allowedEntries of the repeat item to the
     * allowedEntries of the existing item(the one before it in the list).
     * @param atts - the attributes to retur
     * @return - the Attributes combined so that each one has a unique name and contains
     * all allowedEntries from before.
     */
    private TreeSet<Attribute> combineSameName(TreeSet<Attribute> atts)
    {
        //The Set to Return
        TreeSet<Attribute> toReturn = new TreeSet<Attribute>();

        //Will copy the passed in hashSet into an ArrayList so it is easier to use.
        ArrayList<Attribute> temp = new ArrayList<Attribute>();
        temp.addAll(atts);

        //Loop through each element finding all repeat elements farther in the list
        for(int i = 0; i< temp.size(); i++)
        {
            //Loop ahead of the current index to find any repeat names
            for(int j = i + 1; j< temp.size(); j++)
            {
                //If the names match than the item at position j is a repeat so...
                //add all of the allowed entries in at position j to position i.
                if(temp.get(i).getName().equals(temp.get(j).getName()))
                {
                    temp.get(i).getAllowedEntries().addAll(temp.get(j).getAllowedEntries());
                    temp.remove(j);
                    j--;
                }
            }
        }
        //Put the results back into a TreeSet and return
        toReturn.addAll(temp);
        return toReturn;
    }
    //</editor-fold>
}
